package com.paradogs.common.utils;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.util.Assert;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author: yumi
 * @date: 2024/2/8  17:12
 * @Description: TODO
 */
@Slf4j
public class ObjectUtils extends BeanUtils {

    /**
     * 深克隆
     * 复制 100 次，jprotobuf >> 1ms, fastjson2 >> 100ms
     * 复制 10000 次，jprotobuf >> 20ms, fastjson2 >> 140ms
     * 复制 1 千万次, jprotobuf == fastjson2
     * 缺点：
     * Protobuf: 没有被 JProtobuf 相关注解修饰的属性，不能复制
     *
     * 建议使用 kryo ?
     */
    public static <T> T clone(T obj) {
        if (obj == null) { return null; }
//        return (T) ProtoUtils.decode(ProtoUtils.encode(obj), obj.getClass());
        return (T) JSON.parseObject(JSON.toJSONString(obj), obj.getClass());
    }

    /**
     * 重写 BeanUtils.copyProperties()，增加复制时不复制 null 值
     * @param source
     * @param target
     * @throws BeansException
     */
    public static void copyProperties(Object source, Object target) throws BeansException {
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");
        Class<?> actualEditable = target.getClass();
        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
        for (PropertyDescriptor targetPd : targetPds) {
            if (targetPd.getWriteMethod() != null) {
                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null && sourcePd.getReadMethod() != null) {
                    try {
                        Method readMethod = sourcePd.getReadMethod();
                        if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                            readMethod.setAccessible(true);
                        }
                        Object value = readMethod.invoke(source);
                        // 这里判断以下value是否为空 当然这里也能进行一些特殊要求的处理 例如绑定时格式转换等等
                        if (value != null) {
                            Method writeMethod = targetPd.getWriteMethod();
                            if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                writeMethod.setAccessible(true);
                            }
                            writeMethod.invoke(target, value);
                        }
                    } catch (Throwable ex) {
//                        throw new FatalBeanException("Could not copy properties from source to target", ex);
                    }
                }
            }
        }
    }


    /** 将对象转换成数组形式方便存储，待优化：暂时不支持嵌套对象 */
    public static Object[] inst(Object obj) {
        if (obj == null) {
            return new Object[0];
        }

        Field[] fields = obj.getClass().getDeclaredFields();
        Object[] inst = new Object[fields.length];
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            field.setAccessible(true);
            try {
                inst[i] = field.get(obj);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return inst;
    }

    /** 二维数组转 Map | int */
    public static Map<Integer, Integer> arr2Map(Integer[][] arr) {
        if (arr == null) return new LinkedHashMap<>();
        LinkedHashMap<Integer, Integer> map = new LinkedHashMap<>();
        for (Integer[] child : arr) {
            map.merge(child[0], child[1], Integer::sum);
        }
        return map;
    }

}
